package crew.gwt.client.services.util;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.rpc.AsyncCallback;

import crew.gwt.client.message.ServicesMessages;
import crew.gwt.client.widgets.MessagePopupPanel;


/**
 * 
 * Originally designed to centralise the busy icon show/hide functionality
 * but enhanced to handle server call retries in case of failure first time
 * around. Also provides a centralised place to handle onFailure logic.
 * 
 * If the server can respond with a failure payload containing the contents
 * of a redirected URL (for example a login page when RPC call occurs after
 * session has expired) then this is a good place to centrally control the
 * corresponding logic. More generically it is a good place to centralise 
 * any onFailure handling of the server exception.
 * 
 * Inspired by:
 * http://stackoverflow.com/questions/1309436/
 * 		automatic-loading-indicator-when-calling-an-async-function/
 * 		1311604#1311604
 * 
 * @author Paul
 *
 * @param <T>
 */
public abstract class ServerCall<T> implements AsyncCallback<T> {

	private static Command loginRequestCommand;
	private final ServicesMessages servicesMessages = GWT.create(ServicesMessages.class);

	/** 
	 * Call the service method using asyncCallback as the callback. 
	 */
	protected abstract void callService(AsyncCallback<T> asyncCallback);

	/**
	 * Send the call to the server with retry on failure
	 * @param retryCount the number of retries
	 */
	public void call(int retryCount) {
		attempt(retryCount);
	}    

	/**
	 * Send the call to the server with no retry
	 */
	public void call() {
		attempt(0);
	}    

	private void attempt(final int retriesLeft) {

		showLoading();

		callService(new AsyncCallback<T>() {

			public void onFailure(Throwable t) {
				GWT.log(t.toString(), t);
				if (retriesLeft <= 0) {
					hideLoading();
					ServerCall.this.onFailure(t);
				} else {
					attempt(retriesLeft - 1);
				}
			}

			public void onSuccess(T result) {
				hideLoading();
				ServerCall.this.onSuccess(result);
			}

		});
	}


	protected void showLoading() {
		GWT.log("Show loading message");
	}

	protected void hideLoading() {
		GWT.log("Hide loading message");
	}

	public void onFailure(Throwable t) {

		GWT.log("Failure: " + t);

		new MessagePopupPanel(false, servicesMessages.operationFailed() ).show();
		
		/*
		 * From GWT documentation of InvocationException:
		 * 
		 * Occurs when a service invocation did not complete cleanly. 
		 *
		 * A service invocation completes cleanly if
		 * 	1.	A response is returned from the service, or
		 * 	2.	An exception generated within the service is successfully received 
		 * 		and re-thrown in the client.
		 * 
		 * A service invocation can fail to complete cleanly for many reasons, including
		 * 	1.	The network connection to the server is unavailable
		 * 	2.	The host web server is not available
		 * 	3.	The server is not available
		 *
		 * Note that it is possible for this exception to be thrown even if the service 
		 * was invoked successfully on the server. This could be the case, for example, 
		 * if a network failure happened after the invocation request was sent but before 
		 * the response was received.
		 * 
		 */
		
//		if (t instanceof StatusCodeException){
//			Window.alert("Erreur\nDésolé, cette opération a échoué !");
//		} else if (t instanceof InvocationException){
//
//			/*
//			 * Check if the exception was actually our login page content due to redirection
//			 */
////			onLoginRequested();
//			if (t.getMessage().split("<!--\\s*SPRING-SECURITY-LOGIN-PAGE\\s*-->").length > 1){
//				onLoginRequested();
//			} else {
//				Window.alert("Erreur\nDésolé, cette opération a échoué !");
//			}
//		} else if (t instanceof ServerSideException){
//			Window.alert("Operation impossible : " + t.toString());
//		}
	}

	private void onLoginRequested() {
		GWT.log("Login requested by server");
		ServerCall.loginRequestCommand.execute();
	}

	/**
	 * Provide a command to be executed when onLoginRequested is called
	 * Examples: 
	 * 	*	native $wnd.location.replace("/login.html")
	 * 	*	Window.open a new popup with a login form
	 * 	* 	Use GWT popup to ask you for an in situ login
	 * 
	 * Various pros and cons as far as complexity vs preservation of
	 * history state
	 * 
	 * @param cmd
	 */
	public static void setLoginRequestCommand(Command cmd) {
		ServerCall.loginRequestCommand = cmd;
	}

}